La dos.library Programmando in C la dos.library costituisce l'unica eccezione a quanto detto prima sulle librerie; infatti non vi è bisogno di aprirla perché l'apertura e la chiusura vengono automaticamente effettuati dal compilatore; questo per mantenere la compabilità con lo standard ANSI C (e quindi garantire la portabilità dei programmi); infatti lo standard prevede di gestire files senza bisogno di aprire e chiudere alcunché; apertura e chiusura che devono quindi essere realizzate in maniera trasparente al programmatore. Come avrete capito la dos.library mette a disposizione le funzioni per gestire il DOS (Disk Operating System = Sistema Operativo del Disco), cioè gestione dei files, directory, drive et similia. Iniziamo subito con la gestione dei files, ed in particolare con la funzione che permette di aprirne uno: file = Open(nomefile,modalità); dove nomefile è il puntatore alla stringa contenente il nome del file o del dispositivo (tipo CON: o RAW:) completo di path; modalità indica la modalità appunto con cui aprire il file e può valere MODE_OLDFILE, per aprire un file già esistente per la lettura e scrittura (se il file non esiste verrà segnalato un errore), MODE_NEWFILE che crea un nuovo file per lettura e scrittura (se il file esiste già verra inizializzato comunque); ed infine MODE_READWRITE che apre un file, sempre per operazioni sia di lettura che di scrittura in modalità spartita (ciò significa che lo stesso file potrà essere aperto anche da altri processi, mentre per le precedenti modalità il file veniva bloccato, cioè altri processi non possono accedervi fino a quando non viene chiuso dal programma che lo usa attualmente); file è il puntatore ad una struttura FileHandle che contiene tutte le informazioni relative al file aperto; informazioni che necessitano a tutte le altre funzioni che operano sui file, per cui tale puntatore dovrà essere passato a tutte le funzioni che verranno chiamate successivamente; se il valore ritornato da Open è nullo, allora il file non è stato aperto per qualche motivo, per cui si è verificato un errore; vediamo un esempio: struct FileHandle *Fh; . . if ((Fh = Open("ram:pippo",MODE_OLDFILE)) == NULL) { printf("Non posso aprire il file pippo.\n"); exit(1); } Dopo aver finito di utilizzare il file, occorre chiuderlo con la seguente funzione: Close(file); dove file è il puntatore a FileHandle ritornato dalla rispettiva funzione Open. La volta scorsa abbiamo parlato delle funzioni della dos.library per aprire e chiudere un file, le prossime istruzioni da esaminare riguardano la gestione di quest'ultimo; i files nell'AmigaDOS vengono gestiti sia ad accesso sequenziale che casuale (random); ciò significa che all'apertura del file, il cursore che segna la lettura è posizionato all'inizio di quest'ultimo e, man mano che si effettuano operazioni di lettura o di scrittura, quest'ultimo si posiziona alla fine della lunghezza dell'ultima operazione realizzata; in questa maniera si sfrutta un accesso sequenziale al file, esiste tuttavia un metodo per posizionarsi in un qualsiasi punto del file e utilizzare così un accesso casuale. La prima funzione da analizzare è Read che, come si può intuire dal nome, realizza l'operazione di lettura: LungAttuale = Read(file,buffer,lunghezza); dove file è il puntatore alla struttura FileHandle ritornata dalla funzione Open, buffer è un puntatore ad un buffer in memoria che conterrà i dati da leggere, e lunghezza il numero di bytes da leggere dal file; il parametro ritornato LungAttuale indica il numero di bytes effettivamente letti dall'operazione di Read; normalmente tale valore è positivo e serve a verificare che il numero di bytes letti equivale effettivamente a quello richiesto (in caso contrario si sarà raggiunto l'end-of-file); se tale numero vale zero significa che ci si trova già sull'end-of-file e quindi non è possibile leggere alcun byte; se tale valore risulta infine -1 significa che si è verificato un errore; osserviamo immediatamente un esempio: struct FileHandle *file; LONG len; UBYTE buffer[1000]; . . len = Read(file,buffer,1000); if (len < 0) printf("Errore\n"); else if (len < 1000) printf("Letti meno bytes, raggiunto EOF"); questo esempio tenta di leggere 1000 bytes dal file identificato dalla struttura FileHandle denominato 'file' e di inserirlo nel buffer denominato 'buffer' (che nella fattispecie è creato come vettore); se il valore ritornato len è negativo viene stampato su video un errore, se è inferiore a 1000 viene avvisato l'utente sempre con un messaggio. La seconda istruzione da esaminare è Write che serve per la scrittura in un file; la sintassi è: LungRitorno = Write(file,buffer,lunghezza); dove 'file' ricopre il solito significato, 'buffer' è il puntatore al buffer contenente i dati da scrivere e 'lunghezza' indica il numero di byte da scrivere; 'LungRitorno' indica il numero di bytes realmente scritti sul file; anche in questo caso in presenza di errore il valore ritornato è -1; l'esempio viene riportato qui di seguito: struct FileHandle *file; LONG len; UBYTE buffer[1000]; . . len = Write(file,buffer,1000); if (len < 0) printf("Errore\n"); Le istruzioni Read e Write sono non bufferizzate, ciò significa che se leggo un solo byte alla volta il dos accederà ogni volta al file impiegando così molto tempo; una vecchia soluzione per risolvere questo problema era leggere un blocco di bytes riponendolo in un buffer e da lì prelevare i singoli caratteri; dalla versione 2.0 del sistema operativo (V36) sono previste però due funzioni che permettono di leggere e scrivere un byte alla volta con bufferizzazione implementata dal DOS: FGetC e FPutC. La prima preleva un carattere dalla poszione corrente del file e il secondo inserisce un carattere nella posizione corrente del file; la sintassi di queste è: carattere = FGetC(file); dove 'carattere' è il byte letto dal file puntato da 'file'. carattere1 = FPutC(file,carattere2); dove 'carattere2' è il byte da scrivere nel file puntato da 'file'; il valore ritornato 'carattere1', o è identico a carattere2 o corrisponde a EOF se si è verificato errore. Dato che con queste operazioni viene effettuata una bufferizzazione, se dopo la chiamata di una di queste si ha intenzione di utilizzare un Read o un Write (che non sono bufferizzate), occorre effettuare un operazione di scarico del buffer: Flush; successo = Flush(file); dove il valore ritornato indica il successo dell'operazione. Un'altra operazione importante legata a quelle bufferizzate è UnGetC che permette da inserire nel buffer un qualsiasi byte e che quindi verrà acquisito nella prossima operazione di lettura (se questa è bufferizzata); valore = UnGetC(file,carattere); 'carattere' è il byte che viene inserito nel buffer di I/O; se questi vale -1 allora verrà reinserito nel buffer l'ultimo byte letto (sempre da operazione bufferizzata e vale a dire FGetC); il parametro ritornato 'valore', indica il carattere inserito nel buffer o FALSE se tale carattere non può essere inserito nel buffer. Come abbiamo accennato, vi è la possibilità di accedere casualmente al file mediante un istruzione che ci permette di spostarci in un punto qualsiasi di quest'ultimo: Seek la cui sintassi è: vecchiapos = Seek(file,posizione,modalità); Il valore 'vecchiapos' ritornato indica la posizione assoluta (rispetto all'inizio del file) attuale, prima che l'operazione di spostamento abbia luogo; se il valore ritornato risulta -1, lo spostamento non è stato effettuato per qualche motivo (indice uscito fuori dai limiti del file); 'modalità' indica il tipo di riferimento da adottare per lo spostamento, che può valere OFFSET_BEGINNING (inizio del file), OFFSET_CURRENT (corrente posizione del file) o OFFSET_END (fine del file); 'posizione' è il valore che indica la nuova posizione da assumere rispetto al riferimento indicato da 'modalità' e può essere sia negativo che positivo, vediamo qualche esempio: struct FileHandle *file; LONG vecpos,pos; . . vecpos = Seek(file,0,OFFSET_BEGINNING); /* Posizionati all'inizio del file */ . vecpos = Seek(file,0,OFFSET_END); /* Posizionati alla fine del file */ . vecpos = Seek(file,1,OFFSET_CURRENT); /* Posizionati di un byte avanti alla posizione corrente */ . vecpos = Seek(file,-1,OFFSET_END); /* Posizionati un byte prima della fine del file */ Fino ad adesso è stato sempre indicato come dopo ogni operazione DOS, sia possibile rilevare se quest'ultima è incorsa in un errore o meno, ma come fare per capire qual è l'errore che è stato causato? La funzione DOS che si occupa di identificare l'errore è IoErr: (vp1)errore = IoErr(); (vp5)Dove errore è il codice dell'errore verificatosi; attenzione però se l'operazione ha avuto successo e quindi non si è verificato alcun errore, nulla si sà sul valore ritornato. Ci sono molte altre funzioni della dos.library alcuni delle quali importanti, che corrispondono a nuove caratteristiche del 2.0 (assegnamenti multipli ecc.), ma quelle esaminate sono quelle che utilizzerete più spesso.